package com.ihg.hiex.admin.security;

import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.demo2do.core.visitor.ObjectVisitor;
import com.ihg.hiex.admin.entity.security.Login;
import com.ihg.hiex.admin.service.account.AccountService;
import com.ihg.hiex.admin.service.account.AdministratorService;
import com.ihg.hiex.admin.service.account.SupervisorService;
import com.ihg.hiex.entity.user.Account;
import com.ihg.hiex.entity.user.Administrator;
import com.ihg.hiex.entity.user.Role;
import com.ihg.hiex.entity.user.Supervisor;

/**
 * 
 * @author downpour
 * 
 */
public class ApplicationAuthenticationProvider implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(ApplicationAuthenticationProvider.class);

    @Autowired
    private AccountService accountService;
    
    @Autowired
    private AdministratorService administratorService;
    
    @Autowired
    private SupervisorService supervisorService;
    
    @Autowired
    private ObjectVisitor objectVisitor;
    
    /* (non-Javadoc)
     * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
     */
    @SuppressWarnings({"unchecked"})
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        
        // ======= initialize other user properties =======
        Account account = accountService.loadByName(username.toLowerCase());
        
        // 0. validate account whether valid
        if(account == null) {
            throw new UsernameNotFoundException("未能找到用户");
        }
        
        String authority = account.getAuthority();
        if (!StringUtils.contains(authority, Role.ADMIN.name()) && !StringUtils.contains(authority, Role.SUPERVISOR.name()) && !StringUtils.contains(authority, Role.FACILITATOR.name()) && !StringUtils.contains(authority, Role.COACH.name())) {
            throw new InsufficientAuthenticationException("用户权限不足");
        }

        logger.info("ApplicationAuthenticationProvider#loadUserByUsername() - principal [" + account.getEmail() + " ] is found to login. ");
        
        String email = account.getEmail().toLowerCase();
        if (StringUtils.contains(authority, Role.ADMIN.name())) {           // login is admin
            
            // 1. construct Login by Account & Admin
            Administrator administrator = administratorService.loadByEmail(email, Role.ADMIN.toString());
            if (administrator == null) {
                throw new UsernameNotFoundException("未能找到Admin用户");
            }
            
            Login login = new Login(account, administrator);
            login.init(Role.ADMIN.name());
            
            // 2. initialize role based resources
            Map<String, List<String>> resources = (Map<String, List<String>>) objectVisitor.evaluate("secure.resources['" + Role.ADMIN.name() + "']");
            if(MapUtils.isNotEmpty(resources)) {
                login.initResources(resources);
            }
            
            return login;
            
        } else if (StringUtils.contains(authority, Role.FACILITATOR.name())) {           // login is Facilitator
            
            // 1. construct Login by Account & Admin
            Administrator administrator = administratorService.loadByEmail(email, Role.FACILITATOR.toString());
            if (administrator == null) {
                throw new UsernameNotFoundException("未能找到Facilitator用户");
            }
            
            Login login = new Login(account, administrator);
            login.init(Role.FACILITATOR.name());
            
            // 2. initialize role based resources
            Map<String, List<String>> resources = (Map<String, List<String>>) objectVisitor.evaluate("secure.resources['" + Role.FACILITATOR.name() + "']");
            if(MapUtils.isNotEmpty(resources)) {
                login.initResources(resources);
            }
            
            return login;
        } else if (StringUtils.contains(authority, Role.COACH.name())) {           // login is Coach
            
            // 1. construct Login by Account & Admin
            Administrator administrator = administratorService.loadByEmail(email, Role.COACH.toString());
            if (administrator == null) {
                throw new UsernameNotFoundException("未能找到Coach用户");
            }
            
            // 2. construct Login by Account
            Login login = new Login(account, administrator);
            login.init(Role.COACH.name());
            
            // 3. initialize role based resources
            Map<String, List<String>> resources = (Map<String, List<String>>) objectVisitor.evaluate("secure.resources['" + Role.COACH.name() + "']");
            if(MapUtils.isNotEmpty(resources)) {
                login.initResources(resources);
            }
            
            return login;
        } else {                                                            // login is supervisor
            
            // 1. construct Login by Account & Supervisor
            Supervisor supervisor = supervisorService.loadByEmail(email);
            if (supervisor == null) {
                throw new UsernameNotFoundException("未能找到Supervisor用户");
            }
            
            Login login = new Login(account, supervisor);
            login.init(Role.SUPERVISOR.name());
            
            // 2. initialize role based resources
            Map<String, List<String>> resources = (Map<String, List<String>>) objectVisitor.evaluate("secure.resources['" + Role.SUPERVISOR.name() + "']");
            if(MapUtils.isNotEmpty(resources)) {
                login.initResources(resources);
            }
            
            return login;
            
        }
        
    }

}
